//package com.ruoyi.activity.service;
//
//import com.ruoyi.activity.domain.Coupon;
//import com.ruoyi.activity.domain.Order;
//import com.ruoyi.activity.domain.Product;
//import com.ruoyi.activity.mapper.CouponMapper;
//import com.ruoyi.activity.mapper.OrderMapper;
//import com.ruoyi.activity.mapper.ProductMapper;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.stereotype.Service;
//
//import java.util.Date;
//
///**
// * 订单服务
// */
//@Service
//public class OrderService {
//
//    @Autowired
//    private RedisTemplate<Object, Object> redisTemplate;
//
//    @Autowired
//    private ProductMapper productMapper;
//
//    @Autowired
//    private CouponMapper couponMapper;
//
//    @Autowired
//    private OrderMapper orderMapper;
//
//    /**
//     * 用户下单
//     * @param userId 用户ID
//     * @param productId 商品ID
//     * @param couponId 优惠券ID
//     * @return 是否下单成功
//     */
//    public boolean createOrder(Long userId, Long productId, Long couponId) {
//        // 查询商品信息
//        Product product = productMapper.selectById(productId);
//        if (product == null) {
//            // 商品不存在，返回失败
//            return false;
//        }
//        // 检查商品类型是否为1或2（仅允许指定类型商品参与活动）
//        if (!(product.getType() == 1 || product.getType() == 2)) {
//            // 商品类型不符，返回失败
//            return false;
//        }
//        // 查询券信息
//        Coupon coupon = couponMapper.selectById(couponId);
//        // 校验券归属和状态
//        if (coupon == null || !coupon.getUserId().equals(userId) || coupon.getStatus() != 0) {
//            // 券不存在、不是本人、已用过，返回失败
//            return false;
//        }
//        // 构造商品库存key
//        String stockKey = "product:" + productId + ":stock";
//        // 扣减库存，原子操作防止超卖
//        Long stock = redisTemplate.opsForValue().decrement(stockKey);
//        if (stock == null || stock < 0) {
//            // 库存不足，回滚并返回失败
//            redisTemplate.opsForValue().increment(stockKey);
//            return false;
//        }
//        // 标记券为已使用
//        coupon.setStatus(1);
//        couponMapper.updateById(coupon);
//        // 新建订单对象，设置用户、商品、券、状态、时间
//        Order order = new Order();
//        order.setUserId(userId);
//        order.setProductId(productId);
//        order.setCouponId(couponId);
//        order.setStatus(0); // 0待支付
//        order.setCreateTime(new Date());
//        // 插入订单到数据库
//        orderMapper.insert(order);
//        // 构造订单超时key
//        String orderTimeoutKey = "order:timeout";
//        // 计算超时时间（当前时间+30分钟）
//        long timeout = System.currentTimeMillis() + 30 * 60 * 1000;
//        // 将订单ID和超时时间放入Redis有序集合，实现延迟取消
//        redisTemplate.opsForZSet().add(orderTimeoutKey, order.getId(), timeout);
//        // 下单成功
//        return true;
//    }
//
//    /**
//     * 用户主动取消订单
//     * @param userId 用户ID
//     * @param orderId 订单ID
//     * @return 是否取消成功
//     */
//    public boolean cancelOrder(Long userId, Long orderId) {
//        // 查询订单信息
//        Order order = orderMapper.selectById(orderId);
//        // 校验订单归属和状态
//        if (order == null || !order.getUserId().equals(userId) || order.getStatus() != 0) {
//            // 订单不存在、不是本人、已取消/完成，返回失败
//            return false;
//        }
//        // 设置订单状态为已取消
//        order.setStatus(2);
//        orderMapper.updateById(order);
//        // 归还商品库存
//        String stockKey = "product:" + order.getProductId() + ":stock";
//        redisTemplate.opsForValue().increment(stockKey);
//        // 查询券信息
//        Coupon coupon = couponMapper.selectById(order.getCouponId());
//        if (coupon != null) {
//            // 设置券为已返还
//            coupon.setStatus(2);
//            couponMapper.updateById(coupon);
//        }
//        // 从Redis延迟队列移除该订单
//        String orderTimeoutKey = "order:timeout";
//        redisTemplate.opsForZSet().remove(orderTimeoutKey, orderId);
//        // 取消成功
//        return true;
//    }
//}
package com.ruoyi.activity.service;

import com.ruoyi.activity.domain.Coupon;
import com.ruoyi.activity.domain.Order;
import com.ruoyi.activity.domain.Product;
import com.ruoyi.activity.mapper.CouponMapper;
import com.ruoyi.activity.mapper.OrderMapper;
import com.ruoyi.activity.mapper.ProductMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 高并发安全的订单服务实现
 * 解决数据一致性问题，支持高并发场景
 */
@Slf4j  // 使用Lombok的日志注解
@Service
public class OrderService {

    // 库存扣减Lua脚本（原子操作）
    private static final String STOCK_DECR_SCRIPT =
            "local key = KEYS[1] " +
                    "local quantity = tonumber(ARGV[1]) " +
                    "if quantity == nil then return 0 end " +  // 检查quantity有效性
                    "local stock = tonumber(redis.call('get', key)) " +  // 修正括号
                    "if stock == nil then stock = 0 end " +     // 显式处理nil
                    "if stock >= quantity then " +
                    "   redis.call('decrby', key, quantity) " +
                    "   return 1 " +
                    "else " +
                    "   return 0 " +
                    "end";
    // 库存回滚Lua脚本（原子操作）
    private static final String STOCK_INCR_SCRIPT =
            "local key = KEYS[1] " +
                    "local quantity = tonumber(ARGV[1]) " +
                    "if quantity == nil then return 0 end " +  // 检查quantity有效性
                    "redis.call('incrby', key, quantity) " +
                    "return 1";                    // 返回成功标志

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate; // Redis操作模板

    @Autowired
    private ProductMapper productMapper; // 商品数据访问

    @Autowired
    private CouponMapper couponMapper;   // 优惠券数据访问

    @Autowired
    private OrderMapper orderMapper;     // 订单数据访问

    /**
     * 用户下单（高并发安全版）
     * @param userId 用户ID
     * @param productId 商品ID
     * @param couponId 优惠券ID
     * @return 是否下单成功
     */
    @Transactional
    public boolean createOrder(Long userId, Long productId, Long couponId) {
        // 1. 校验商品是否存在且类型正确
        Product product = productMapper.selectById(productId);
        if (product == null) {
            log.warn("商品不存在: productId={}", productId);
            return false;
        }
        if (!(product.getType() == 1 || product.getType() == 2)) {
            log.warn("商品类型不符: productId={}, type={}", productId, product.getType());
            return false;
        }

        // 2. 获取优惠券分布式锁（防止同一优惠券被多个请求同时使用）
        String couponLockKey = "lock:coupon:" + couponId;
        boolean lockAcquired = Boolean.TRUE.equals(
                redisTemplate.opsForValue().setIfAbsent(
                        couponLockKey,
                        "LOCKED",
                        10, TimeUnit.SECONDS  // 锁有效期10秒
                )
        );

        if (!lockAcquired) {
            log.warn("获取优惠券锁失败: couponId={}", couponId);
            return false;
        }

        try {
            // 3. 校验优惠券状态
            Coupon coupon = couponMapper.selectById(couponId);
            if (coupon == null) {
                log.warn("优惠券不存在: couponId={}", couponId);
                return false;
            }
            if (!coupon.getUserId().equals(userId)) {
                log.warn("优惠券不属于用户: userId={}, couponUserId={}", userId, coupon.getUserId());
                return false;
            }
            if (coupon.getStatus() != 0) {
                log.warn("优惠券状态无效: status={}", coupon.getStatus());
                return false;
            }

            // 4. 原子扣减Redis库存
            String stockKey = "stock:product:" + productId;
            Long stockResult = redisTemplate.execute(
                    new DefaultRedisScript<>(STOCK_DECR_SCRIPT, Long.class),
                    Collections.singletonList(stockKey), // KEYS列表
                    1 // ARGV列表（扣减数量）
            );

            // 处理库存扣减结果
            if (stockResult == null || stockResult == 0) {
                log.warn("库存不足: productId={}", productId);
                return false;
            }

            // 5. 异步更新数据库库存（最终一致性）
            asyncUpdateProductStock(productId, -1);

            // 6. 更新优惠券状态（使用乐观锁）
            int couponUpdateCount = couponMapper.updateStatusWithOldStatus(
                    couponId,
                    0,  // 期望原状态（未使用）
                    1   // 新状态（已使用）

            );

            if (couponUpdateCount == 0) {
                log.error("优惠券状态更新失败: couponId={}", couponId);
                // 回滚Redis库存
                redisTemplate.execute(
                        new DefaultRedisScript<>(STOCK_INCR_SCRIPT, Long.class),
                        Collections.singletonList(stockKey),
                        "1"
                );
                return false;
            }

            // 7. 创建订单记录
            Order order = new Order();
            order.setUserId(userId);
            order.setProductId(productId);
            order.setCouponId(couponId);
            order.setStatus(0); // 0=待支付
            order.setCreateTime(new Date());
            orderMapper.insert(order);

            // 8. 设置订单超时（30分钟后自动取消）
            String orderExpireKey = "order:expire:" + order.getId();
            redisTemplate.opsForValue().set(
                    orderExpireKey,
                    "pending",
                    30, TimeUnit.MINUTES
            );

            log.info("订单创建成功: orderId={}, userId={}", order.getId(), userId);
            return true;
        } catch (Exception e) {
            log.error("创建订单异常", e);
            return false;
        } finally {
            // 9. 无论成功失败都释放锁
            redisTemplate.delete(couponLockKey);
        }
    }

    /**
     * 异步更新商品库存（保证最终一致性）
     * @param productId 商品ID
     * @param delta 库存变化量（正数增加，负数减少）
     */
    @Async  // 异步执行，不阻塞主线程
    public void asyncUpdateProductStock(Long productId, int delta) {
        final int MAX_RETRY = 3;  // 最大重试次数
        int retryCount = 0;
        boolean updated = false;

        while (retryCount < MAX_RETRY && !updated) {
            try {
                // 获取当前商品库存
                Product product = productMapper.selectById(productId);
                if (product == null) {
                    log.error("更新库存失败: 商品不存在, productId={}", productId);
                    break;
                }

                // 计算新库存（确保不小于0）
                int newStock = product.getStock() + delta;
                if (newStock < 0) {
                    log.error("库存不足无法更新: productId={}, delta={}", productId, delta);
                    break;
                }

                // 使用乐观锁更新库存
                int updateCount = productMapper.updateStockWithVersion(
                        productId,
                        product.getStock(),  // 当前库存作为乐观锁条件
                        newStock,
                        product.getVersion() // 当前版本号
                );

                if (updateCount > 0) {
                    log.info("库存更新成功: productId={}, newStock={}", productId, newStock);
                    updated = true;
                } else {
                    log.warn("库存更新冲突: productId={}, 重试中...", productId);
                    retryCount++;
                    Thread.sleep(50); // 短暂等待后重试
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("库存更新线程被中断", e);
                break;
            } catch (Exception e) {
                log.error("库存更新异常", e);
                retryCount++;
            }
        }

        if (!updated) {
            log.error("库存更新失败: productId={}, 已达到最大重试次数", productId);
            // 这里可以添加告警通知或加入补偿队列
        }
    }

    /**
     * 用户取消订单（高并发安全版）
     * @param userId 用户ID（可为null，表示系统自动取消）
     * @param orderId 订单ID
     * @return 是否取消成功
     */
    @Transactional
    public boolean cancelOrder(Long userId, Long orderId) {
        // 1. 查询订单
        Order order = orderMapper.selectById(orderId);
        if (order == null) {
            log.warn("订单不存在: orderId={}", orderId);
            return false;
        }

        // 系统自动取消时跳过用户校验
        if (userId != null && !order.getUserId().equals(userId)) {
            log.warn("订单不属于用户: userId={}, orderUserId={}", userId, order.getUserId());
            return false;
        }

        // 2. 检查订单状态（只能取消待支付订单）
        if (order.getStatus() != 0) {
            log.warn("订单状态不可取消: status={}", order.getStatus());
            return false;
        }

        // 3. 获取订单分布式锁（防止多个请求同时取消同一订单）
        String orderLockKey = "lock:order:" + orderId;
        boolean lockAcquired = Boolean.TRUE.equals(
                redisTemplate.opsForValue().setIfAbsent(
                        orderLockKey,
                        "LOCKED",
                        10, TimeUnit.SECONDS
                )
        );

        if (!lockAcquired) {
            log.warn("获取订单锁失败: orderId={}", orderId);
            return false;
        }

        try {
            // 4. 双重检查订单状态（避免在获取锁期间状态被修改）
            Order freshOrder = orderMapper.selectById(orderId);
            if (freshOrder.getStatus() != 0) {
                log.warn("订单状态已变更: currentStatus={}", freshOrder.getStatus());
                return false;
            }

            // 5. 更新订单状态为已取消（使用乐观锁）
            int updateCount = orderMapper.updateStatus(
                    orderId,
                    0,  // 期望原状态（待支付）
                    2   // 新状态（已取消）
            );

            if (updateCount == 0) {
                log.warn("订单状态更新失败: orderId={}", orderId);
                return false;
            }

            // 6. 回退商品库存（Redis原子操作）
            String stockKey = "product:stock:" + order.getProductId();
            redisTemplate.execute(
                    new DefaultRedisScript<>(STOCK_INCR_SCRIPT, Long.class),
                    Collections.singletonList(stockKey),
                    "1"
            );

            // 7. 异步更新数据库库存
            asyncUpdateProductStock(order.getProductId(), 1);

            // 8. 回退优惠券状态
            int couponUpdate = couponMapper.updateStatusWithOldStatus(
                    order.getCouponId(),
                    1,  // 原状态（已使用）
                    2   // 新状态（已返还）
            );

            if (couponUpdate == 0) {
                log.warn("优惠券状态回退失败: couponId={}", order.getCouponId());
                // 这里不返回失败，因为订单取消已完成
            }

            // 9. 移除订单超时标记
            String expireKey = "order:expire:" + orderId;
            redisTemplate.delete(expireKey);

            log.info("订单取消成功: orderId={}, userId={}", orderId, userId);
            return true;
        } catch (Exception e) {
            log.error("取消订单异常", e);
            return false;
        } finally {
            // 10. 释放分布式锁
            redisTemplate.delete(orderLockKey);
        }
    }
}